#!/bin/bash
#
# lc_servip - script for verifying the service IP and the real
#             interface IP in a remote host are in the same subnet
#
###############################################################################

# Usage
usage() {
	cat >&2 <<EOF

Usage:  `basename $0` <service IPaddr> <hostname>
	
	service IPaddr		the IP address to failover
	hostname		the hostname of the remote node

EOF
	exit 1
}

# Check arguments
if [ $# -lt 2 ]; then
        usage
fi

# Remote command
REMOTE=${REMOTE:-"ssh -x -q"}

# Check whether the reomte command is pdsh
is_pdsh() {
        if [ "${REMOTE}" = "${REMOTE#*pdsh}" ]; then
                return 1
        fi

        return 0
}

#
# inSameIPsubnet serviceIPaddr interfaceIPaddr mask
#
# Given two IP addresses and a subnet mask determine if these IP
# addresses are in the same subnet. If they are, return 0, else return 1.
#
inSameIPsubnet() {
	declare -i n
	declare -ia mask 
	declare -ia ip1 ip2		# IP addresses given
	declare -i quad1 quad2		# calculated quad words

	#
	# Remove '.' characters from dotted decimal notation and save
	# in arrays. i.e.
	#
	#	192.168.1.163 -> array[0] = 192
	#	                 array[1] = 168
	#	                 array[2] = 1
	#	                 array[3] = 163
	#
	let n=0
	for quad in $(echo $1 | awk -F. '{print $1 " " $2 " " $3 " " $4}')
	do
		ip1[n]=$quad
	  	let n=n+1
	done

	let n=0
	for quad in $(echo $2 | awk -F. '{print $1 " " $2 " " $3 " " $4}')
	do
	  	ip2[n]=$quad
	  	let n=n+1
	done

	let n=0
	for quad in $(echo $3 | awk -F. '{print $1 " " $2 " " $3 " " $4}')
	do
	  	mask[n]=$quad
	  	let n=n+1
	done

	#
	# For each quad word, logically AND the IP address with the subnet
	# mask to get the network/subnet quad word.  If the resulting
	# quad words for both IP addresses are the same they are in the 
	# same IP subnet.
	#
	for n in 0 1 2 3
	do
	  	let $((quad1=${ip1[n]} & ${mask[n]}))
	  	let $((quad2=${ip2[n]} & ${mask[n]}))

	  	if [ $quad1 != $quad2 ]; then
			echo >&2 $"`basename $0`: Service IP address $1 and"\
				  "real interface IP address $2 are in"\
				  "different subnets!"
	    		return 1	# in different subnets
	  	fi
	done

	return 0	# in the same subnet, all quad words matched
}

#
# findInterface IPaddr hostname
#
# Given a target IP address and a hostname, find the interface in which 
# this address is configured.  If found return 0, if not return 1.  The
# interface name is returned to stdout.
#
findInterface() {
	declare ret_line
	declare line
	declare intf
	declare addr
	declare state

	declare target=$1
	declare hostname=$2

	while read ret_line
	do
		set -- ${ret_line}
		is_pdsh && shift
		intf="$1"
		shift
		line="$*"

		while read line
	  	do
	    		if [ "$line" = "" ]; then	# go to next interface
	      			continue 2
	    		fi

	    		set - $line
	    		addr=
	    		while [ $# -gt 0 ]; do
	      			case $1 in
	        		addr:*)
	          			addr=${1##addr:}
	          			if [ -n "$addr" -a "$addr" = "$target" ]
					then
	            				echo $intf
	            				return 0
	          			fi
	          			;;
	      			esac
	      			shift
	    		done
	  	done
	done < <(${REMOTE} $hostname /sbin/ifconfig)

	echo >&2 "`basename $0`: Cannot find the interface in which" \
		  "$target is configured in the host $hostname!"
	return 1
}

#
# findNetmask interface hostname
#
# Given an interface find the netmask addresses associated with it.
# Return 0 when found, else return 1. The netmask is returned to stdout.
#
findNetmask() {
	declare ret_line
	declare line
	declare addr
	declare target=$1
	declare hostname=$2

	while read ret_line
	do
		set -- ${ret_line}
		is_pdsh && shift
		line="$*"

		set - $line

	  	while [ $# -gt 0 ]; do
	    		case $1 in
	      		Mask:*)
	        		echo ${1##*:} 	# return netmask addr
	        		return 0 
	        		;;
 	    		esac
	    		shift
	  	done
	done < <(${REMOTE} $hostname /sbin/ifconfig $target)

	echo >&2 "`basename $0`: Cannot find the netmask associated with" \
		  "the interface $target in the host $hostname!"
	return 1 
}

#
# check_srvIPaddr serviceIPaddr hostname
#
# Given a service IP address and hostname, check whether the service IP address
# and the real interface IP address of hostname are in the same subnet. 
# If they are, return 0, else return 1.
#
check_srvIPaddr() {
	declare real_IPaddr
	declare real_intf
	declare netmask
	declare srv_IPaddr=$1
	declare hostname=$2

	# Get the corresponding IP address of the hostname from /etc/hosts table
	real_IPaddr=`egrep "[[:space:]]$hostname([[:space:]]|$)" /etc/hosts \
                     | awk '{print $1}'`
        if [ -z "$real_IPaddr" ]; then
                echo >&2 "`basename $0`: Hostname $hostname does not exist in" \
                         "the local /etc/hosts table!"
                return 1
        fi

        if [ ${#real_IPaddr} -gt 15 ]; then
                echo >&2 "`basename $0`: More than one IP address line" \
                         "corresponding to $hostname in the local"  \
			 "/etc/hosts table!"
                return 1
        fi

	# Get the interface in which the real IP address is configured
	real_intf=$(findInterface $real_IPaddr $hostname)
	if [ $? -ne 0 ]; then
		return 1
	fi
	real_intf=${real_intf%%:*}

	# Get the netmask address associated with the real interface
	netmask=$(findNetmask $real_intf $hostname)
	if [ $? -ne 0 ]; then
		return 1
	fi

	# Determine if the service IP address and the real IP address
	# are in the same subnet
	inSameIPsubnet $srv_IPaddr $real_IPaddr $netmask
	if [ $? -ne 0 ]; then
		return 1
	fi

	return 0
}

# Check service IP address
if ! check_srvIPaddr $1 $2; then
	exit 1
fi
exit 0
